package main

import (
	"global/model"
	"github.com/ngaut/log"
	"os"
	"global/core"
	"srv/analysis/lib/coin"
	"global/libs/structs"
	"sync"
	"strings"
	"time"
	"srv/analysis/lib/exchange"
)

type Analysis struct {
	// Context 类型
	Ctx 			*core.Context

	// 保存所有币种信息
	Coins 			map[string]*model.Coin

	// 保存所有交易所
	Exchanges 		map[string]*model.Exchange

	// 保存交易所对象的交易市场
	Markets 		map[string]map[string]interface{}

	// 保存交易所对应的交易对
	Symbols 		map[string]map[string]structs.Symbol

	// 币种处理程序
	CoinHandler 		*coin.CoinHandler
	// 交易所数据处理程序
	ExchangeHandler 	*exchange.ExchangeHandle

	// 处理并发操作
	wg 				sync.WaitGroup
	mux 			sync.Mutex

	// 计时器
	coinCheckerC 		*time.Ticker
	exchangeCheckerC 	*time.Ticker
}

func NewAnalysis() *Analysis {
	analysis := &Analysis{
		Ctx: 				ctx,
		Coins: 				make(map[string]*model.Coin),
		Markets: 			make(map[string]map[string]interface{}),
		Symbols: 			make(map[string]map[string]structs.Symbol),
		CoinHandler: 		coin.NewCoinHandle(),
		ExchangeHandler: 	exchange.NewExchangeHandle(),

		coinCheckerC: 		time.NewTicker(10 * time.Second),
		exchangeCheckerC: 	time.NewTicker(10 * time.Second),
	}
	return analysis
}

// 初始化
func (a *Analysis) Init() {
	// 需要按时更新下面的数据
	a.Coins = a.loadAllCoins()
	a.Exchanges = a.loadAllExchanges()

	a.loadAllSymbols()
}

// 加载所有交易所的交易对
func (a *Analysis) loadAllSymbols() {
	a.wg.Add(len(a.Exchanges))
	for _, exg := range a.Exchanges {
		go func(e *model.Exchange) {
			defer a.wg.Done()

			conn := a.Ctx.GetBaseRedis().Get()
			defer conn.Close()

			symbols, err := core.GetSymbolsByExchange(conn, e.Slug)
			if err != nil {
				log.Errorf("Error: %s", err.Error())
				os.Exit(1)
			}

			symbolMap := make(map[string]structs.Symbol)
			market := make(map[string]interface{})
			for _, s := range symbols {
				from := strings.ToLower(s.From)
				to := strings.ToLower(s.To)
				key := from + "_" + to

				// 保存交易所所有交易对
				symbolMap[key] = s
				// 保存交易所所有交易市场
				market[to] = nil
			}

			a.mux.Lock()
			a.Symbols[e.Slug] = symbolMap
			a.Markets[e.Slug] = market
			a.mux.Unlock()

		}(exg)
	}
	a.wg.Wait()
}

// 获取所有交易所
func (a *Analysis) loadAllExchanges() map[string]*model.Exchange {
	exchanges, err := model.ExchangeModel.AllEnabled()
	if err != nil {
		log.Errorf("Error: load all exchanges failed: %s", err)
		os.Exit(1)
	}

	es := make(map[string]*model.Exchange)
	for _, v := range exchanges {
		slug := strings.ToLower(v.Slug)
		es[slug] = v
	}

	return es
}

// 获取所有币
func (a *Analysis) loadAllCoins() map[string]*model.Coin {
	coins, err := model.CoinModel.FindAll()
	if err != nil {
		log.Errorf("Error: load all coins failed: %s", err)
		os.Exit(1)
	}
	coinMaps := make(map[string]*model.Coin)
	for _, v := range coins {
		slug := strings.ToLower(v.Symbol)
		coinMaps[slug] = v
	}
	return coinMaps
}

// 入口
func (a *Analysis) Run() {
	go a.coinChecker()
	go a.exchangeChecker()

	a.handlerCoins()
	a.handlerExchanges()
}

func (a *Analysis) coinChecker() {
	for t := range a.coinCheckerC.C {
		_ = t
		newCoins := a.loadAllCoins()

		// 先看有没有修改的，再看有没有增减的
		// 循环现有币种，找出修改和删除的币种
		for slug, oldCoin := range a.Coins {
			if newCoin, ok := newCoins[slug]; ok {
				// 无变化
				if *newCoin == *oldCoin {
					continue
				}

				// 说明已经修改
				a.CoinHandler.UpdateCoin(coin.CoinUpdate, newCoin)
			} else {
				// 已删除
				a.CoinHandler.UpdateCoin(coin.CoinDel, newCoin)
			}
		}

		for slug, newCoin := range newCoins {
			if _, ok := a.Coins[slug]; !ok {
				a.CoinHandler.UpdateCoin(coin.CoinAdd, newCoin)
			}
		}

		a.Coins = newCoins
	}
}

func (a *Analysis) exchangeChecker() {
	for t := range a.exchangeCheckerC.C {
		_ = t
		newExchanges := a.loadAllExchanges()

		// 先看有没有修改的，再看有没有增减的
		// 循环现有币种，找出修改和删除的币种
		for slug, oldExchange := range a.Exchanges {
			if newExchange, ok := newExchanges[slug]; ok {
				// 无变化
				if *newExchange == *oldExchange {
					continue
				}

				// 说明已经修改
				a.ExchangeHandler.UpdateExchange(exchange.ExchangeOptUpdate, newExchange)
			} else {
				// 已删除
				a.ExchangeHandler.UpdateExchange(exchange.ExchangeOptDel, newExchange)
			}
		}

		for slug, newExchange := range newExchanges {
			if _, ok := a.Coins[slug]; !ok {
				a.ExchangeHandler.UpdateExchange(exchange.ExchangeOptAdd, newExchange)
			}
		}

		a.Exchanges = newExchanges
	}
}

// 处理币种/市值
func (a *Analysis) handlerCoins()  {

	a.CoinHandler.Init(
		a.Ctx,
		a.Coins,
		a.Exchanges,
		a.Markets,
		a.Symbols,
	)
	a.CoinHandler.Run()
}

// 处理交易所
func (a *Analysis) handlerExchanges()  {
	a.ExchangeHandler.Init(
		a.CoinHandler,
		a.Ctx,
		a.Markets,
		a.Exchanges,
		a.Symbols,
	)
	a.ExchangeHandler.Run()
}